package cn.xydpeng.springframework.beans.factory.support;

import cn.xydpeng.springframework.beans.BeansException;
import cn.xydpeng.springframework.beans.factory.FactoryBean;
import cn.xydpeng.springframework.lang.Nullable;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xydpeng
 * @version v1.0
 * @date: 2022/10/20
 * @description:
 */
public class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    /**
     * Cache of singleton objects created by FactoryBeans: FactoryBean name to object.
     *  --- 缓存FactoryBean.getObject()创建的单例对象，FactoryBean单例对象本身会缓存到DefaultSingletonBeanRegistry.singletonObjects中
     */
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);

    @Nullable
    protected Object getCachedObjectForFactoryBean(String beanName) {
        return this.factoryBeanObjectCache.get(beanName);
    }

    protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName,boolean shouldPostProcess) {
        if (factory.isSingleton() && containsSingleton(beanName)) {
            synchronized (getSingletonMutex()) {
                //检查缓存
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    //调用FactoryBean.getObject() 生成bean
                    object = doGetObjectFromFactoryBean(factory, beanName);
                }
                //放入缓存
                if (containsSingleton(beanName)) {
                    this.factoryBeanObjectCache.put(beanName, object);
                }
                //applyBeanPostProcessorsAfterInitialization，后置处理FactoryBean.getObject()方式新生成的object, 扩展点交给了子类处理
                if(shouldPostProcess){

                    object = postProcessObjectFromFactoryBean(object, beanName);
                }
                return object;
            }
        } else {
            //非Singleton，直接创建新的bean
            Object object = doGetObjectFromFactoryBean(factory, beanName);
            return object;
        }

    }


    private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeansException {
        Object object = null;
        try {
            object = factory.getObject();
        } catch (Exception ex) {
            throw new BeansException("FactoryBean getObject error,beanName:" + beanName, ex);
        }
        return object;
    }

    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
        return object;
    }

}
